package cz.spock.cms.service;

import java.util.List;

import org.springframework.stereotype.Service;

import com.google.appengine.api.blobstore.BlobKey;
import com.google.appengine.api.datastore.Blob;
import com.googlecode.objectify.Key;

import cz.spock.cms.entity.BinaryDataHolder;
import cz.spock.core.entity.Account;
import cz.spock.core.service.BaseService;
import cz.spock.exception.InvalidDataException;
import cz.spock.exception.InvalidInputException;
import cz.spock.exception.SpockRuntimeException;

/**
 * provides logic for working with data files (mostly binary data files)
 * 
 * API for both blobstore and datastore
 * - objectify uses only datastore
 * - datastore has limit 0.5MB per file
 * - blobstore would be better for bigger files, it is said that files are served from nearest servers according to location 
 * 
 * @author miso
 *
 */
@Service
public class DataService extends BaseService {
    
    
    /**
     * creates new BinaryDataHolder which holds reference to blobstore
     * 
     * @param uploaderId
     * @param uri
     * @param title
     * @param desc
     * @param ctype
     * @param bkey
     * @return
     */
    public BinaryDataHolder createBlobstoreData(Long uploaderId, String uri, String title, String desc, String ctype, BlobKey bkey) {        
        return this.save(this.setUri(uri, this.setUploader(new Key<Account>(Account.class, uploaderId), this.setTitle(title, this.setDescription(desc, this.setContentType(ctype, this.setBlobstoreData(bkey, new BinaryDataHolder())))))));
    }
    
    /**
     * persists given object and returns object with assigned id
     */
    public BinaryDataHolder save(BinaryDataHolder object) {
        if(object == null) return null;
        object.setId(dao.ofy().put(object).getId());
        return object;
    }
    
    public BinaryDataHolder setUploader(Key<Account> uploaderKey, BinaryDataHolder object) {
        if(uploaderKey == null) {
            log.fine("uploader key is null");            
            throw new InvalidInputException("uploader is required", 400);
        }
        if(object == null) {
            log.severe("binary data holder is null");
            throw new SpockRuntimeException("binary data holder is null", 500);            
        }
        if(object.getUploader() != null) {
            
        }
        object.setUploader(uploaderKey);
        return object;
    }
    
    public BinaryDataHolder setUri(String uri, BinaryDataHolder object) {
        if(uri == null || uri.isEmpty()) {
            log.fine("uri is null or empty");            
            throw new InvalidInputException("uri is null or empty", 400);
        }
        if(object == null) {
            log.severe("binary data holder is null");
            throw new SpockRuntimeException("binary data holder is null", 500);            
        }
        if(!this.isUriUnique(uri)) {
            log.fine("uri is not unique");
            throw new InvalidDataException("uri is not unique", 400);
        }
        object.setUri(uri);
        return object;
    }
    
    public BinaryDataHolder setTitle(String title, BinaryDataHolder object) {
        if(title == null) {
            title = "";
        }
        if(object == null) {
            log.severe("binary data holder is null");
            throw new SpockRuntimeException("binary data holder is null", 500);            
        }
        object.setTitle(title);
        return object;
    }
    
    public BinaryDataHolder setDescription(String desc, BinaryDataHolder object) {
        if(desc == null) {
            desc = "";
        }
        if(object == null) {
            log.severe("binary data holder is null");
            throw new SpockRuntimeException("binary data holder is null", 500);            
        }
        object.setTitle(desc);
        return object;
    }
    
    public BinaryDataHolder setContentType(String ctype, BinaryDataHolder object) {
        if(ctype == null) {
            ctype = "";
        }
        if(object == null) {
            log.severe("binary data holder is null");
            throw new SpockRuntimeException("binary data holder is null", 500);            
        }
        object.setContentType(ctype);
        return object;
    }
    
    public BinaryDataHolder setDatastoreData(Blob blob, BinaryDataHolder object) {
        if(blob == null) {
            log.fine("blob is null or empty");            
            throw new InvalidInputException("blob is null or empty", 400);
        }
        if(object == null) {
            log.severe("binary data holder is null");
            throw new SpockRuntimeException("binary data holder is null", 500);            
        }
        object.setDatastoreData(blob);
        return object;
    }

    public BinaryDataHolder setBlobstoreData(BlobKey blobkey, BinaryDataHolder object) {
        if(blobkey == null) {
            log.fine("blobkey is null or empty");            
            throw new InvalidInputException("blobkey is null or empty", 400);
        }
        if(object == null) {
            log.severe("binary data holder is null");
            throw new SpockRuntimeException("binary data holder is null", 500);            
        }
        object.setBlobstoreData(blobkey);
        return object;
    }
    
    /**
     * returns true if given uri is unique
     * 
     * @param uri
     * @return
     */
    public boolean isUriUnique(String uri) {
        if(uri == null) return false;
        if(dao.ofy().query().filter("uri", uri).get() == null) return true;
        return false;
    }
        
    /**
     * finds files according to uploader
     * 
     * @param userId
     * @return
     */
    public List<BinaryDataHolder> findFileByUser(Long userId) {
        if(userId == null) return null;
        return dao.ofy().query(BinaryDataHolder.class).filter("uploader", userId).list();
    }
    
    /**
     * finds for file with given uri
     * 
     * @param uri
     * @return
     */
    public BinaryDataHolder findFileByUri(String uri) {
        if(uri == null) return null;
        return dao.ofy().query(BinaryDataHolder.class).filter("uri", uri).get();
    }

    /**
     * can be potentially performance killer!
     * @return all files
     */
    public List<BinaryDataHolder> findAllFiles() {
        return dao.ofy().query(BinaryDataHolder.class).list();
    }
    
}
